"use strict";

var __importDefault = this && this.__importDefault || function (mod) {
  return mod && mod.__esModule ? mod : {
    "default": mod
  };
};

Object.defineProperty(exports, "__esModule", {
  value: true
});

var isMergeableObjectUtil_1 = __importDefault(require("./isMergeableObjectUtil"));

function emptyTarget(val) {
  return Array.isArray(val) ? [] : {};
}

function cloneUnlessOtherwiseSpecified(value, options) {
  return options.clone !== false && options.isMergeableObject(value) ? deepmerge(emptyTarget(value), value, options) : value;
}

function defaultArrayMerge(target, source, options) {
  return target.concat(source).map(function (element) {
    return cloneUnlessOtherwiseSpecified(element, options);
  });
}

function getMergeFunction(key, options) {
  if (!options.customMerge) {
    return deepmerge;
  }

  var customMerge = options.customMerge(key);
  return typeof customMerge === 'function' ? customMerge : deepmerge;
}

function getEnumerableOwnPropertySymbols(target) {
  return Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(target).filter(function (symbol) {
    return target.propertyIsEnumerable(symbol);
  }) : [];
}

function getKeys(target) {
  return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target));
}

function propertyIsOnObject(object, property) {
  try {
    return property in object;
  } catch (_) {
    return false;
  }
} // Protects from prototype poisoning and unexpected merging up the prototype chain.


function propertyIsUnsafe(target, key) {
  return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
  && !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
  && Object.propertyIsEnumerable.call(target, key)); // and also unsafe if they're nonenumerable.
}

function mergeObject(target, source, options) {
  var destination = {};

  if (options.isMergeableObject(target)) {
    getKeys(target).forEach(function (key) {
      destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
    });
  }

  getKeys(source).forEach(function (key) {
    if (propertyIsUnsafe(target, key)) {
      return;
    }

    if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
      destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
    } else {
      destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
    }
  });
  return destination;
}

function deepmerge(x, y, options) {
  options = options || {};
  options.arrayMerge = options.arrayMerge || defaultArrayMerge;
  options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
  options.isMergeableObject = options.isMergeableObject || isMergeableObjectUtil_1.default;
  var sourceIsArray = Array.isArray(y);
  var targetIsArray = Array.isArray(x);
  var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;

  if (!sourceAndTargetTypesMatch) {
    return cloneUnlessOtherwiseSpecified(y, options);
  } else if (sourceIsArray) {
    return options.arrayMerge(x, y, options);
  } else {
    return mergeObject(x, y, options);
  }
}

function isObject(o) {
  return Object.prototype.toString.call(o) === '[object Object]';
}

(function (deepmerge) {
  function all(objects, options) {
    if (!Array.isArray(objects)) {
      throw new Error('first argument should be an array');
    }

    return objects.reduce(function (prev, next) {
      return deepmerge(prev, next, options);
    }, {});
  }

  deepmerge.all = all; //判断是否是真实地object对象

  function isPlainObject(o) {
    var ctor, prot;
    if (isObject(o) === false) return false;
    ctor = o.constructor;
    if (ctor === undefined) return true;
    prot = ctor.prototype;
    if (isObject(prot) === false) return false;

    if (prot.hasOwnProperty('isPrototypeOf') === false) {
      return false;
    }

    return true;
  }

  deepmerge.isPlainObject = isPlainObject;
})(deepmerge || (deepmerge = {}));

exports.default = deepmerge;